use std::net::{IpAddr, Ipv4Addr, SocketAddr};
use std::sync::Arc;

use fastmcp_rs::{
    FastMcpServer,
    start_http,
    tool::ToolResponse,
    mcp_tool, mcp_register_tools, mcp_prompt_text, mcp_register_prompts, mcp_response, mcp_text,
};
use serde_json::{Value, json};
use tokio::signal;

// 将工具的定义抽离到 main 之外，便于复用与测试
fn fetch_url_tool(client: Arc<reqwest::Client>) -> fastmcp_rs::tool::ToolDefinition {
    let client = client.clone();
    mcp_tool! {
        name: "fetch_url",
        handler: move |_, payload: Value| {
            let client = client.clone();
            async move {
                let url = payload
                    .get("url")
                    .and_then(Value::as_str)
                    .ok_or_else(|| {
                        fastmcp_rs::FastMcpError::InvalidInvocation(
                            "expected string argument 'url'".into(),
                        )
                    })?
                    .to_string();

                let response = client
                    .get(&url)
                    .send()
                    .await
                    .map_err(fastmcp_rs::FastMcpError::handler_error)?;

                let status = response.status();
                let body = response
                    .text()
                    .await
                    .map_err(fastmcp_rs::FastMcpError::handler_error)?;

                let snippet = body.chars().take(500).collect::<String>();
                Ok(mcp_response!([ mcp_text!(format!("Status: {status}\n\n{}", snippet)) ]))
            }
        },
        description: "Fetches the contents of a URL",
        parameters_json: {
            "type": "object",
            "required": ["url"],
            "properties": {
                "url": {
                    "type": "string",
                    "format": "uri"
                }
            }
        },
        annotations: { "category": json!("net") },
    }
}

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    tracing_subscriber::fmt()
        .with_env_filter(tracing_subscriber::EnvFilter::from_default_env())
        .init();

    let server = FastMcpServer::builder()
        .name("RustFetch")
        .instructions("Use the fetch_url tool to retrieve web content.")
        .build()
        .into_shared();

    let http_client = Arc::new(reqwest::Client::new());
    mcp_register_tools!(server, [ fetch_url_tool(http_client.clone()) ]);

    mcp_register_prompts!(server, [
        mcp_prompt_text! {
            name: "fetch_page",
            role: "user",
            text: "Fetch the page at {{ url }}",
            description: "Ask the assistant to fetch a web page",
            parameters: json!({
                "type": "object",
                "required": ["url"],
                "properties": {
                    "url": { "type": "string", "format": "uri" }
                }
            }),
        }
    ]);

    let addr = SocketAddr::new(IpAddr::V4(Ipv4Addr::LOCALHOST), 0);
    let handle = start_http(server, addr).await?;
    println!("Streamable HTTP server ready at http://{}", handle.addr());

    signal::ctrl_c().await?;
    handle.shutdown().await;
    Ok(())
}
